 #include <xpm/netpad.xpm>
 #include <xpm/red.xpm>
 #include <xpm/green.xpm>
 #include <xpm/invisible.xpm>
 #include <xpm/orange.xpm>
 #include <xpm/off.xpm>
 #include <xpm/online.xpm>
 #include <xpm/busy.xpm>
 #include <xpm/away.xpm>
 #include <xpm/invited.xpm>
 #include <xpm/search.xpm>
 #include <xpm/star.xpm>
 #include <xpm/mail.xpm>

 #include <controls.h>
 #include <common.h>
 #include <netpad.h>
 #include <jabber.h>
 #include <util.h>
 
 extern wxCondition * m_condition;
 extern wxMutex     * m_mutex;
 extern connection    c_list[];

 /* binary wav data for incoming pings */

 static const unsigned char THUD_WAV[] = {
 0x52, 0x49, 0x46, 0x46, 0x58, 0x07, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 
 0x66, 0x6d, 0x74, 0x20, 0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 
 0x22, 0x56, 0x00, 0x00, 0x93, 0x2b, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 
 0x20, 0x00, 0xf4, 0x03, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 
 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0x00, 0xf0, 0x00,
 0x00, 0x00, 0xcc, 0x01, 0x30, 0xff, 0x88, 0x01, 0x18, 0xff, 0x66, 0x61, 
 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0xdf, 0x09, 0x00, 0x00, 0x64, 0x61, 
 0x74, 0x61, 0x00, 0x06, 0x00, 0x00, 0x01, 0x10, 0x00, 0x05, 0x00, 0x01, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x0f, 0x00, 0x10, 0x0f, 
 0x00, 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x0f, 0x01, 0x00, 0xf0, 0xfe, 0xfe, 0xbd, 0xae, 0xcf, 0x01, 0x52, 0x43, 
 0x35, 0x23, 0x42, 0x23, 0x13, 0x00, 0x0d, 0xbc, 0xcd, 0xae, 0xce, 0xce, 
 0xec, 0xee, 0xee, 0xe0, 0x00, 0x24, 0x43, 0x53, 0x52, 0x42, 0x33, 0x34, 
 0x13, 0x13, 0x00, 0xee, 0xdb, 0xcc, 0xdb, 0xed, 0xce, 0xcf, 0xdf, 0xd0, 
 0xf0, 0x02, 0x43, 0x54, 0x34, 0x35, 0x23, 0x41, 0x32, 0x21, 0x21, 0x0d, 
 0xdd, 0x9d, 0xcc, 0xdc, 0xdc, 0xdd, 0xec, 0xfe, 0xe0, 0x01, 0x42, 0x53, 
 0x44, 0x25, 0x23, 0x23, 0x23, 0x21, 0x20, 0xfd, 0xbd, 0xae, 0xcd, 0xbf, 
 0xcf, 0xde, 0xdf, 0xd0, 0xe0, 0x02, 0x43, 0x62, 0x52, 0x42, 0x33, 0x34, 
 0x31, 0x32, 0x10, 0x0e, 0xcd, 0xbd, 0xcc, 0xdc, 0xdc, 0xed, 0xed, 0xfe, 
 0x00, 0x32, 0x52, 0x52, 0x35, 0x14, 0x22, 0x32, 0x22, 0x11, 0x00, 0xed, 
 0xcd, 0xda, 0xed, 0xdc, 0xde, 0xdd, 0xef, 0xf0, 0x12, 0x22, 0x43, 0x43, 
 0x35, 0x23, 0x23, 0x21, 0x10, 0x0f, 0xdf, 0xce, 0xcd, 0xdc, 0xce, 0xde, 
 0xcf, 0xfe, 0x00, 0x01, 0x31, 0x24, 0x13, 0x33, 0x22, 0x21, 0x20, 0x10, 
 0xe0, 0xee, 0xee, 0xde, 0xdd, 0xfd, 0xee, 0x0f, 0xf0, 0x02, 0x12, 0x31, 
 0x33, 0x23, 0x32, 0x22, 0x12, 0x10, 0x10, 0xef, 0xfe, 0xfe, 0xdf, 0xee, 
 0xfe, 0xff, 0x00, 0x00, 0x12, 0x21, 0x22, 0x23, 0x13, 0x12, 0x20, 0x20, 
 0x00, 0xf0, 0xef, 0xef, 0xfd, 0xef, 0xef, 0xef, 0x0f, 0xf0, 0x01, 0x10, 
 0x11, 0x12, 0x01, 0x20, 0x01, 0x00, 0x00, 0xe0, 0xff, 0xfe, 0xff, 0xe0, 
 0xef, 0x0f, 0xf0, 0x01, 0x00, 0x10, 0x12, 0x00, 0x11, 0x10, 0x01, 0x00, 
 0xf0, 0xf0, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x01, 0x00, 0x10, 0x20, 0x11, 
 0x10, 0x20, 0x11, 0x00, 0x10, 0x00, 0x0f, 0x0f, 0x00, 0x0f, 0x00, 0xf0, 
 0x10, 0x01, 0x01, 0x01, 0x02, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0xff, 
 0xff, 0x0f, 0x0e, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x10, 0x01, 0x00, 0x01, 
 0x00, 0x00, 0xf0, 0x00, 0xff, 0x00, 0xff, 0x0f, 0x00, 0xf0, 0x00, 0x00, 
 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf0, 0xf0, 0x00, 0x00, 0x0f, 0x00, 
 0x01, 0x00, 0x10, 0x01, 0x10, 0x01, 0x10, 0x00, 0x10, 0x10, 0xf0, 0x00, 
 0x00, 0x00, 0x00, 0xf0, 0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x10, 0x00, 
 0x10, 0x01, 0x00, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x0f, 0x00, 0x00, 
 0xf0, 0x01, 0x00, 0xf0, 0x00, 0x10, 0xf0, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 
 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x0f, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x10, 0x01, 
 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 
 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf0, 0x00, 
 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x0f, 0x01, 0x0f, 0x00, 0x00, 0xf0, 
 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 
 0x0f, 0x01, 0x00, 0x00, 0x01, 0x0f, 0x01, 0x00, 0xf0, 0x00, 0x00, 0x10, 
 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x10, 0x0f, 0x01, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x01, 
 0x0f, 0x01, 0x0f, 0x00, 0x10, 0xf0, 0x00, 0x00, 0x10, 0xf0, 0x10, 0x0f, 
 0x01, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x10, 
 0xf0, 0x00, 0x01, 0x10, 0x00, 0x1b, 0x03, 0x22, 0x03, 0x00, 0x00, 0xf0, 
 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 
 0x01, 0x00, 0xf0, 0x10, 0x0f, 0x00, 0x10, 0xf0, 0x01, 0x00, 0x00, 0x01, 
 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x10, 0xf0, 0x01, 
 0x00, 0x0f, 0x01, 0x01, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0xf0, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 
 0x00, 0xf0, 0x0f, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 
 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 
 0x00, 0x01, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xf0, 
 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf0, 0x01, 0x00, 0x00, 0x00, 
 0x10, 0x0f, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x10, 0xe0, 0x10, 0x00, 0xf0, 
 0x01, 0x00, 0xf0, 0x01, 0x00, 0xf0, 0x00, 0x00, 0xf0, 0x00, 0x1f, 0x00, 
 0x00, 0xf0, 0x01, 0x00, 0xf0, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0xf0, 
 0x10, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x10, 0x00, 0x01, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 
 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x00, 0x00, 
 0x00, 0xf0, 0x10, 0x00, 0x00, 0xf0, 0x10, 0x0f, 0x00, 0x00, 0x10, 0xf0, 
 0x00, 0x00, 0x10, 0xf0, 0x00, 0x00, 0xf0, 0x01, 0x0f, 0x0f, 0x00, 0x10, 
 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x01, 0x0f, 0x00, 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x01, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x01, 0x0f, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 
 0x10, 0xff, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 
 0x00, 0xf0, 0x00, 0xf0, 0x10, 0x0f, 0x00, 0x10, 0x00, 0xf0, 0x10, 0x00, 
 0x0f, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x01, 
 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x1f, 0x00, 0x00, 0x01, 0x0f, 0x00, 
 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x10, 
 0xf0, 0x00, 0x10, 0x0f, 0x00, 0x10, 0x00, 0xf0, 0x10, 0xf0, 0x00, 0x10, 
 0xf0, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 
 0xf0, 0x01, 0x00, 0xf0, 0x10, 0x00, 0x0f, 0x01, 0x0f, 0x00, 0x10, 0xf0, 
 0x00, 0x00, 0x01, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 
 0x00, 0x00, 0x01, 0x0f, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x10, 0x0f, 0x00, 
 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf0, 0x01, 0x00, 
 0xf0, 0x01, 0x00, 0x0f, 0x00, 0x10, 0xf0, 0x00, 0x10, 0xf0, 0x00, 0x01, 
 0x0e, 0x01, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x10, 0xf0, 0x00, 0x10, 0x00, 0x00, 
 0x10, 0xf0, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x10, 0xf0, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x01, 0x0f, 0x00, 0x01, 
 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x00, 0xf0, 0x01, 0x00, 0xf0, 
 0x01, 0x0f, 0x01, 0x0f, 0x00, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0xf0, 0x10, 0x0f, 0x00, 0x10, 0x00, 0xf0, 0x10, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x0f, 0x01, 0x00, 0x01, 0x0f, 0x00, 0x01, 0x00, 0x04, 0x10, 
 0x00, 0x29, 0xff, 0x28, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0xf0, 0xff, 0xf0, 
 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x11, 
 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 
 0x21, 0x11, 0x11, 0x11, 0x11, 0x11, 0x01, 0x10, 0x10, 0x10, 0x10, 0x10, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0x0f, 0xf0, 0xff, 0xff, 
 0xff, 0xff, 0x0f, 0xff, 0x0f, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 
 0xff, 0xff, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 
 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x01, 0x11, 0x11, 0x11, 
 0x10, 0x11, 0x11, 0x10, 0x11, 0x11, 0x11, 0x10, 0x11, 0x11, 0x11, 0x01, 
 0x11, 0x10, 0x10, 0x10, 0x10, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x0f, 0x0f, 0xf0, 0xf0, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 
 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x00, 
 0xf0, 0x0f, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x11, 0x11, 0x11, 0x10, 0x11, 
 0x11, 0x11, 0x11, 0x11, 0x01, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 
 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 
 0xff, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 
 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 
 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x49, 0x53, 0x54, 0x46, 0x00, 
 0x00, 0x00, 0x49, 0x4e, 0x46, 0x4f, 0x49, 0x43, 0x52, 0x44, 0x0b, 0x00, 
 0x00, 0x00, 0x31, 0x39, 0x39, 0x36, 0x2d, 0x30, 0x32, 0x2d, 0x31, 0x32, 
 0x00, 0x00, 0x49, 0x45, 0x4e, 0x47, 0x0d, 0x00, 0x00, 0x00, 0x42, 0x69, 
 0x6c, 0x6c, 0x20, 0x57, 0x6f, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x00, 0x00, 
 0x49, 0x53, 0x46, 0x54, 0x10, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x75, 0x6e, 
 0x64, 0x20, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x20, 0x34, 0x2e, 0x30, 0x00, 
 0x63, 0x75, 0x65, 0x20, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 0x4c, 0x49, 0x53, 0x54, 0x74, 0x00, 0x00, 0x00, 0x61, 0x64, 0x74, 0x6c, 
 0x6c, 0x74, 0x78, 0x74, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
 0xde, 0x09, 0x00, 0x00, 0x72, 0x67, 0x6e, 0x20, 0x00, 0x00, 0x00, 0x00, 
 0x00, 0x00, 0x00, 0x00, 0x6c, 0x74, 0x78, 0x74, 0x14, 0x00, 0x00, 0x00, 
 0x02, 0x00, 0x00, 0x00, 0xde, 0x09, 0x00, 0x00, 0x72, 0x67, 0x6e, 0x20, 
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x61, 0x62, 0x6c, 
 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x65, 0x63, 0x6f, 
 0x72, 0x64, 0x20, 0x54, 0x61, 0x6b, 0x65, 0x20, 0x30, 0x30, 0x31, 0x00, 
 0x6c, 0x61, 0x62, 0x6c, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x20, 0x54, 0x61, 0x6b, 0x65, 0x20, 
 0x30, 0x30, 0x32, 0x00, 
 };

 void
  Jabber::xmpp_status(connection *c, wxString show, bool bInvisible)
 { 
   wxString presence = wxString("<presence from='" + *((wxString *)c->j_jid) + "' xml:lang='en'><show>" + show + "</show>" + "</presence>");

   assert (c);
   
   j_send (c, presence);

   if (!show.Cmp("dnd")) {

      j_status->m_bitmap = red_xpm;
      j_status->SetLabel(_("Busy") + " ");

   } else if (!show.Cmp("default") || !show.Cmp("chat")) {

      if (!bInvisible) {

        j_status->m_bitmap = green_xpm;
        j_status->SetLabel(_("Available") + " ");

      } else {

        j_status->m_bitmap = invisible_xpm;
        j_status->SetLabel(_("Invisible") + " ");
      }

   } else if (!show.Cmp("away")) {

      j_status->m_bitmap = orange_xpm;
      j_status->SetLabel(_("Away") + " ");
   }

   j_szTool->Layout();
   j_status->Refresh();
 }

 void
  Jabber::update_title_bar(IMBuddy *buddy, connection *c)
 {
   wxString  *packet = NULL;
   wxString   fullname;
   wxString   image;

   assert (buddy || c);
   
   if (!c) {
     j_sizer->Hide(j_szTool, true);
     c = buddy->b_c;
   }

   buddy ? (packet = buddy->b_vcard) : (j_hover = false , packet = (wxString *)c->j_vcard);

   if (!packet || !(j_sizer->IsShown(j_szTB))) {
     return;
   }

   wxString tofind("<FN>");

   xml_element(*packet, tofind, &fullname);

   j_fn->SetLabel(wxEmptyString);

   if (fullname.Len()) {
     j_fn->SetLabel(fullname);
   }
 
   j_fid->SetLabel(buddy ? *(buddy->b_id): *((wxString *)(c->m_user)));

   j_avatar->SetBitmap(wxNullBitmap);
   
   tofind = "<BINVAL>";

   xml_element(*packet, tofind, &image);

   if (image.Len()) {

     wxMemoryBuffer mBuf = wxBase64Decode(image, wxBase64DecodeMode_Relaxed);

     wxInputStream *pis = new wxMemoryInputStream(mBuf.GetData(), mBuf.GetDataLen());

     wxImage avatar(*pis, wxBITMAP_TYPE_ANY, -1);
    
     j_avatar->SetSize(avatar.GetWidth(), avatar.GetHeight());
    
     j_avatar->SetBitmap(wxBitmap(avatar));
    
     j_avatar->Show();
   }

   j_avatar->Refresh();   
   
   if (buddy) {

     j_status->Hide();

   } else {

     j_status->Show();
   }
   
   if (!buddy) {

     if (!j_friendReq || !j_friendReq->GetCount()) {

       j_fPlus->Hide();
     }

     if (!c->j_nMail) {

       j_mail->Hide();

     } else {
 
       j_mail->Show();
     }
   }

   j_text->Hide();
   
   if (((wxString *)c->j_jid)->Contains("chat.facebook.com")) {

     j_punchline->Hide();
   
   } else {
   
     j_punchline->Show();
   }
   
   j_status->m_data = c;
   j_status->Refresh();
   j_Row1->Layout();
   j_szTool->Layout();
   j_szTB->Layout();
 }

 void
  Jabber::update_roster (connection *c, wxString& roster)
 {
   const char **image = NULL;
   int   len = 0, index = wxNOT_FOUND, nRoster = roster.Len();
  
   assert (c);

   logit_a ("%s(%d): ->\n", __func__, GetCurrentThreadId());

   index = roster.find("jid=\"");

   assert(index != wxNOT_FOUND);
   
   IMBuddy *buddy = 0;

   wxString tip;

   j_contacts->Freeze();
   
   while (index < nRoster) {

     if (roster.Mid(index, strlen("jid=")).Cmp("jid=")) { break; }
     
     tip.Clear();

     image = offline_xpm;

     len = 0;

     index += 5;
 
     while (roster[index] != '"') { len++ , index++; }

     index -= len;

     buddy = (IMBuddy *) calloc(1, sizeof(IMBuddy));

     if (!j_head) {
       j_head = j_tail = buddy;
     }

     j_tail->next = buddy;

     j_tail = buddy;

     buddy->b_id = new wxString(roster.Mid(index, len));

     buddy->b_resource = new wxString();  /* holds different resources for the user logged in from various places */

     buddy->b_chat = new wxString();

     buddy->b_punchline = new wxString();
     
     buddy->b_c = c;

     len = 0;

     while (roster[index] != '/') {  

      /* this loop extracts the friendly name "Jonny Quid" : start */

      if (roster[index + 0] == 'n' && 
          roster[index + 1] == 'a' && 
          roster[index + 2] == 'm' && 
          roster[index + 3] == 'e' && 
          roster[index + 4] == '=') {

         index += 6;

         while (roster[index] != '"') { len++, index++; }

         index -= len;

         buddy->b_name = new wxString(roster.Mid(index, len));

         break;

      } else if (roster[index + 0] == 'a' && 
                 roster[index + 1] == 's' && 
                 roster[index + 2] == 'k' && 
                 roster[index + 3] == '=' && 
                 roster[index + 4] == '"' &&
                 roster[index + 5] == 's' && 
                 roster[index + 6] == 'u') {

        tip.Append(_("Invited: "));
        image = invited_xpm;
        buddy->b_status = status_invalid;

      } else if (roster[index + 0] == 't' && 
                 roster[index + 1] == '=' && 
                 roster[index + 2] == '"' && 
                 roster[index + 3] == 'H') {
         buddy->b_hidden = true;
         /* j_notify(wxString("Hidden User"), *(buddy->b_id)); */
      }
      index++;
     } 

     tip.Append(*(buddy->b_id) + wxString("\n"));

     buddy->b_i = j_contacts->InsertItem(buddy->b_name ?  *(buddy->b_name) : *(buddy->b_id), 
                                         buddy->b_name ? *(buddy->b_id) : wxString(_("Offline")), 
                                         tip, 
                                         image);

     j_contacts->HideItem((buddy->b_isShown = false, buddy->b_i));

     j_contacts->SetItemData(buddy->b_i, buddy);

     j_contacts->SetItemStatusImage(buddy->b_i, off_xpm);
 
     index = roster.find("jid=\"", index);
   }
   
   j_contacts->Thaw();

   j_contacts->szWrap->Layout();
  
   logit_a ("%s(%d): <-\n", __func__, GetCurrentThreadId());
 }

 void
  Jabber::process_xmpp_packet (connection *c, wxString& packet)
 {
   IMBuddy  *buddy = NULL;
   wxString  id, res;

   assert (c);

   /* packet may have multiple [<iq ], [<presence ], [<message ] packets... */

   if (packet.AfterFirst(' ').Find("<iq ") != wxNOT_FOUND ||
       packet.AfterFirst(' ').Find("<presence ") != wxNOT_FOUND ||
       packet.AfterFirst(' ').Find("<message ") != wxNOT_FOUND) {

      int index = wxNOT_FOUND;

      if ((index = packet.AfterFirst(' ').Find("<iq ")) != wxNOT_FOUND) {

        logit_a ("%s(%d): recursing for <iq \n", __func__, GetCurrentThreadId());

      } else if ((index = packet.AfterFirst(' ').Find("<presence ")) != wxNOT_FOUND) {

        logit_a ("%s(%d): recursing for <presence\n", __func__, GetCurrentThreadId());

      } else if ((index = packet.AfterFirst(' ').Find("<message ")) != wxNOT_FOUND) {        

        logit_a ("%s(%d): recursing for <message\n", __func__, GetCurrentThreadId());

      }

      assert (index != wxNOT_FOUND);

      wxString first  = packet.Left(index+1);
      wxString second = packet.Right(packet.Len()-index-1);

      process_xmpp_packet (c, first);
      process_xmpp_packet (c, second);

      return;
   }

   logit_a ("%s(%d): ->\n", __func__, GetCurrentThreadId());

   if (packet.StartsWith("<presence ") ||
       packet.StartsWith("<message ")) {

     wxString tofind("from=");

     xml_element(packet, tofind, &id, true);

     res = id.AfterFirst('/');

     id = id.BeforeFirst('/');

     /* subscribe and unsubscribe presence */
     /* do not have an associated resource */
     /* just assert for the id             */

     assert(id.Len());
     
     if (!res.Len() && id.Contains("chat.facebook.com")) {
       res = "chat.facebook.com";
     }
   }

   if (packet.StartsWith("<presence ")) {

     logit_w (L"processing presence packet from %s resource %s\n", id.wc_str(), res.wc_str());

     wxString tofind("<priority>"), priority;
     long nPriority = 0;
     
     xml_element(packet, tofind, &priority);

     if (priority.Len()) {
       priority.ToLong(&nPriority);
     }

     if (packet.Find("type=\"subscribe\"") != wxNOT_FOUND) {

        j_notify("New friend request", id + " " + _("wants to add you as his contact."));

        if (!j_friendReq) {
          j_friendReq = new wxArrayString();
        }

        j_friendReq->Add(id);

        j_fPlus->SetLabel(" " + wxString::Format(wxT("%i"),j_friendReq->GetCount()));

        j_fPlus->Show();

        this->RefreshRect(j_fPlus->GetRect());

        wxString tip = _("New friend request:") + " ";

        for (unsigned int i = 0; i < j_friendReq->GetCount(); i++) {
          tip.Append("\n" + (*j_friendReq)[i]);
        }

        j_fPlus->SetToolTip(tip);

        j_Row1->Layout();
 
        return;

     } else if (packet.Find("type=\"subscribed\"") != wxNOT_FOUND) {

        j_notify(_(":)"), id + " " + _("has accepted your friend request."));

        return;

     } else if (packet.Find("type=\"unsubscribe\"") != wxNOT_FOUND) {

        j_notify(_(":("), id + " " + _("is unsubscribing from your presence"));

        return;

     } else if (packet.Find("type=\"unsubscribed\"") != wxNOT_FOUND) {

        j_notify(_(":("), id + " " + _("has either denied or cancelled a previously-granted friend request"));

        return;
     }
   
     /* Check if this contact was added via roster update earlier */
     
     buddy = (IMBuddy *) find_node(c, id);

     if (!buddy) {
       buddy = (IMBuddy *) add_node(c, id);
     }

     buddy->b_punchline->Empty();

     tofind = "<status>";

     xml_element(packet, tofind, buddy->b_punchline);

     j_contacts->SetItemText(buddy->b_i, buddy->b_punchline->Len() ? buddy->b_punchline->Trim(false) : *(buddy->b_id), 1);

     if (!id.Cmp(*((wxString *)(c->m_user)))) {

         /* Self presence */

         if (!buddy->b_punchline->Len()) {
           *(buddy->b_punchline) = _("share a message");
         }

         xml_entity_inbound(buddy->b_punchline->Trim(false));

         j_punchline->SetLabel(buddy->b_punchline->Trim(false));

         j_punchline->SetToolTip(buddy->b_punchline->Trim(false));
 
         j_punchline->GetParent()->RefreshRect(j_punchline->GetRect());

         j_szTool->Layout();  
     }

     /* If this particular resource is not already present in resource, prepend it */

     bool bDoStatusChange = true;
     
     if (!(buddy->b_resource->Contains(res))) {

        buddy->b_resource->Prepend(res + "\n");
   
        /* 
         * if the last saved priority is greater than the 
         * priority of this new resource, skip status change
         * for this contact
         */
        if (buddy->b_priority > nPriority) {
        
          bDoStatusChange = false;
        
        } else {
        
          buddy->b_priority = nPriority;
        }
        
       
     }

     wxString tip = *(buddy->b_id) + "\n" + *(buddy->b_resource);

     /* Check for inclusion of update data in Presence: XEP-0153 */

     bool bGetvcard = false;

     if (packet.Contains("vcard-temp:x:update")) {

       wxString hash;

       wxString tofind("<photo>");

       /* 
        * hash is included in the user's presence information as the XML
        * character data of the <photo/> child of an <x/> element qualified
        * by the 'vcard-temp:x:update' namespace
        */

       if (!xml_element(packet, tofind, &hash)) {

         /* 
          * User is either Not Ready to Advertise an Image (no photo element)
          * or has no image to be Advertised (empty photo element)
          */

          logit_a ("<photo> element not found\n");

       } else {

          logit_w (L"<photo> element found image hash %s\n", hash.wc_str());

          if (!buddy->b_hash) {

            logit_a ("handling first presence packet for contact\n");

            buddy->b_hash = new wxString(hash);

            bGetvcard = true;

          } else if (buddy->b_hash->CmpNoCase(hash)) {

            logit_w (L"stored hash mismatch, stored %s\n", buddy->b_hash->wc_str());

            *(buddy->b_hash) = hash;

            bGetvcard = true;

          } else {

          }
       }
     }

     if (bGetvcard) {

          wxString vcard_req = "<iq from='"    +
                               *((wxString *)c->j_jid) +
                               "' id='"        +
                               *(buddy->b_id)  +
                               "' to='"        +
                               *(buddy->b_id)  +
                               "' type='get'><vCard xmlns='vcard-temp'/></iq>";

          j_send (c, vcard_req);
          
          if (!id.Cmp(*((wxString *)(c->m_user)))) {
          
            wxString vself_req = wxString("<iq from='" + 
                                          *((wxString *)(c->j_jid)) +
                                          "' id='vcard_self' type='get'><vCard xmlns='vcard-temp'/></iq>");
                                        
            j_send (c, vself_req);
          }
          
     }

    if (bDoStatusChange)
    { 
     int show = packet.Find("</show>");

     if (packet.Contains("type=\"unavailable\"")) {
 
       /* Remove this resource from the list of accumulated resources */

       buddy->b_resource->Replace(res + "\n", "");

       if (buddy->b_resource->Len() ==  0) {

         /* We have trimmed b_resource for all resources */
 
         if ((g_alert || (buddy->b_flag & 0x01)) &&
             (buddy->b_status != status_offline)) {

            j_notify(*(buddy->b_id), (buddy->b_name ? *(buddy->b_name) : *(buddy->b_id)) + _(" is offline"));
         }

         buddy->b_status = status_offline;

         j_contacts->SetItemStatusImage(buddy->b_i, off_xpm);

         j_contacts->SetItemImage(buddy->b_i, offline_xpm, 0);

         buddy->b_resource->Clear();

         buddy->b_punchline->Clear();

         tip = *(buddy->b_id);

         if (j_hideoffline) {
           j_contacts->Freeze();
           j_contacts->HideItem((buddy->b_isShown = false, buddy->b_i));
           j_contacts->szWrap->Layout();
           j_contacts->Refresh();
           j_contacts->Thaw();
         }

       } else {

         tip = *(buddy->b_id) + "\n" + *(buddy->b_resource);
       }

     } else if (show == wxNOT_FOUND) {
 
       /* if no </show> element is provided, the entity is assumed to be online and available. */

       j_contacts->ShowItem(buddy->b_i, buddy->b_isShown ? false : buddy->b_isShown = true);

       j_contacts->SetItemStatusImage(buddy->b_i, green_xpm, buddy->b_resource);

       if (buddy->b_avatar) {

         j_contacts->SetItemImage(buddy->b_i, 0, buddy->b_avatar);

       } else {         

         j_contacts->SetItemImage(buddy->b_i, online_xpm, 0);
       }

       if ((g_alert || (buddy->b_flag & 0x01)) &&
           (buddy->b_status != status_available)) {

         j_notify(*(buddy->b_id), (buddy->b_name ? *(buddy->b_name) : *(buddy->b_id)) + _(" is online"));
       }
       /* set status after j_notify */
       buddy->b_status = status_available;

     } else if (show != wxNOT_FOUND) {

       wxString attribute, tofind("<show>");

       xml_element(packet, tofind, &attribute);

       if (!attribute.Cmp("dnd")) {

         j_contacts->SetItemStatusImage(buddy->b_i, red_xpm, buddy->b_resource);
     
         if (buddy->b_avatar) {

           j_contacts->SetItemImage(buddy->b_i, 0, buddy->b_avatar);

         } else {         

           j_contacts->SetItemImage(buddy->b_i, busy_xpm, 0);
         }

         if ((g_alert || (buddy->b_flag & 0x01)) &&
             (buddy->b_status != status_busy)) {

           j_notify(*(buddy->b_id), (buddy->b_name ? *(buddy->b_name) : *(buddy->b_id)) + _(" is busy"));
         }

         buddy->b_status = status_busy;

       } else if (!attribute.Cmp("away") || 
                  !attribute.Cmp("xa"))  {

         j_contacts->SetItemStatusImage(buddy->b_i, orange_xpm, buddy->b_resource);

         if (buddy->b_avatar) {

           j_contacts->SetItemImage(buddy->b_i, 0, buddy->b_avatar);

         } else {         

           j_contacts->SetItemImage(buddy->b_i, away_xpm, 0);
         }

         if ((g_alert || (buddy->b_flag & 0x01)) &&
             (buddy->b_status != status_away)) {

           j_notify(*(buddy->b_id), (buddy->b_name ? *(buddy->b_name) : *(buddy->b_id)) + _(" is away"));
         }

         buddy->b_status = status_away;

       } else {

         j_contacts->SetItemStatusImage(buddy->b_i, green_xpm, buddy->b_resource);

         if (buddy->b_avatar) {

           j_contacts->SetItemImage(buddy->b_i, 0, buddy->b_avatar);

         } else {         

           j_contacts->SetItemImage(buddy->b_i, online_xpm, 0);
         }

         if ((g_alert || (buddy->b_flag & 0x01)) &&
             (buddy->b_status != status_available)) {

           j_notify(*(buddy->b_id), (buddy->b_name ? *(buddy->b_name) : *(buddy->b_id)) + _(" is online"));
         }

         buddy->b_status = status_available;
       }

       j_contacts->ShowItem(buddy->b_i, buddy->b_isShown ? false : buddy->b_isShown = true);
     }
    } /* bDoStatusChange */

     if (buddy->b_tab) {
       buddy->b_tab->Refresh();
     }
     
     j_contacts->SetItemTip(buddy->b_i, tip);

   } else if (packet.StartsWith("<message ")) {  /* somebody's pinging us .. */

     logit_w (L"processing ping packet from %s resource %s\n", id.wc_str(), res.wc_str());

     if (!id.Cmp("chat.facebook.com") && packet.Find("<body>") != wxNOT_FOUND) {
       
       /* facebook admin message */

       wxString message = packet.SubString(packet.Find("<body>") + 6, packet.Find("</body>") - 1);

       xml_entity_inbound (message);
       
       j_notify(wxString("Facebook administrative message"), message);
       
       return;
     }
     
     IMBuddy *buddy = (IMBuddy *) find_node(c, id);
     
     if (!buddy) {

       buddy = (IMBuddy *) add_node(c, id); 

       buddy->b_resource = new wxString(res);
     }

     if (packet.Find("<body>") != wxNOT_FOUND) {

       wxString message = packet.SubString(packet.Find("<body>") + 6, packet.Find("</body>") - 1);

       xml_entity_inbound (message);

       if (!buddy->b_tab) {

         buddy->b_tab = new ChatDialog(this,
                                       wxID_ANY,
                                       buddy->b_name ? *(buddy->b_name) : *(buddy->b_id),
                                       wxDefaultPosition,
                                       wxSize(350, 300),
                                       0,
                                       buddy,
                                       wxGetApp().m_frame->p2);
       }

       if (!(buddy->b_tab->m_point).x ||
           !(buddy->b_tab->m_point).y ) {
   
         buddy->b_tab->m_point = GetParent()->ClientToScreen(wxPoint(0,0));

         (buddy->b_tab->m_point).x -= 375;

         (buddy->b_tab->m_point).x < 0 ? (buddy->b_tab->m_point).x = 20 : 1;

         (buddy->b_tab->m_point).y < 0 ? (buddy->b_tab->m_point).y = 20 : 1;
       }

       buddy->b_tab->Move(buddy->b_tab->m_point);

       if (buddy->b_tab->IsIconized()) {

         buddy->b_tab->Maximize(false);

       } else {

         buddy->b_tab->Show();
       }

       buddy->b_chat->Append("\n");

       wxString line;

       line.Append((buddy->b_name ?  *(buddy->b_name) : *(buddy->b_id)) + " ");

       line.Append(message);

       buddy->b_chat->Append(line);

       buddy->b_tab->parse_chat_transcript(line);

       wxMemoryBuffer mBuf;
       wxString tofind("<data>"), b64snip;
       xml_element(packet, tofind, &b64snip);
       
       if (b64snip.Len()) {

         DWORD startTime = GetTickCount();
         
         b64snip = rle_decode(b64snip);

         logit_a("time taken for rle_decode %d seconds", (GetTickCount()-startTime)/1000);         
         
         mBuf = wxBase64Decode(b64snip, wxBase64DecodeMode_Relaxed);

         logit_a("time taken for wxBase64Decode %d seconds", (GetTickCount()-startTime)/1000);    
         
         wxString tofind("<width>"), width, height;
         xml_element(packet, tofind, &width);         
         tofind  = "<height>";
         xml_element(packet, tofind, &height);  
         
         long nWidth = 0, nHeight = 0;
         
         width.ToLong(&nWidth);
         height.ToLong(&nHeight);
         
         assert(nWidth && nHeight);

         logit_a("recieved snip of width %d height %d", nWidth, nHeight);
         
         wxImage *km_snip = new wxImage(nWidth, nHeight, (unsigned char*) mBuf.GetData());     
         
         if (km_snip->IsOk()) {
           buddy->b_tab->ch_transcript->AddImage(*km_snip);
           //km_snip->SaveFile(wxString("C:\\Users\\mamn\\Desktop\\y.png"), wxBITMAP_TYPE_PNG);
         }
       }
       
       wxSound snd;

       snd.Create(sizeof(THUD_WAV), THUD_WAV);

       if (snd.IsOk()) {
         snd.Play(wxSOUND_ASYNC);
       }

       buddy->b_tab->ch_chat->SetFocus();

       buddy->b_tab->RequestUserAttention();

       if (j_contacts->m_autoreply || buddy->b_autoreply) {

         wxString text = buddy->b_autoreply ? *(buddy->b_autoreply) : *((wxString *)(j_contacts->m_autoreply)) ;

         wxString message = wxString("<message from='")                          +
                            *((wxString *)c->j_jid)                              +
                            wxString("' id='")                                   +
                            wxString("sl3nx51f")                                 +
                            wxString("' to='")                                   +
                            *(buddy->b_id)                                       +
                            wxString("' type='chat' xml:lang='en'><body>")       +
                            text                                                 +
                            wxString("</body><image xmlns='netpad:image'><data>" +
                            wxString("nmam")                                     +
                            wxString("</data></image></message>"));

          j_send(c, message);

          buddy->b_chat->Append("\n");

          wxString line;

          line.Append(wxString(_("Me")) + " ");

          if (text.Last() == '\n') {

            line.Append(text.RemoveLast());

          } else {

            line.Append(text);
          }

          buddy->b_chat->Append(line);

          buddy->b_tab->parse_chat_transcript(line);

          buddy->b_tab->ch_chat->Clear();

          buddy->b_tab->ch_chat->SetInsertionPoint(0);
       }
     }

   }  else if ((packet.Find("type=\"set\"") != wxNOT_FOUND) && 
               (packet.Find("google:shared-status") != wxNOT_FOUND)) { 

      /* extract and save the shared status substring recieved from the server */

      wxString extractSS = (packet.AfterFirst('>')).AfterFirst('>');

      assert(extractSS.Len());

      extractSS.Replace("sta:", "");

      if (c->j_ss) {
        delete (wxString *)c->j_ss, c->j_ss = NULL;
      }

      c->j_ss = new wxString("<iq type='set' to='" +
                             *((wxString *)(c->m_user)) + 
                             "' id='ss-2'><query xmlns='google:shared-status' version='2'>" +
                             extractSS);

      process_status(c);

   } else if (((packet.Find("type=\"result\"") != wxNOT_FOUND) || (packet.Find("type=\"error\"") != wxNOT_FOUND)) && 
               (packet.Find("google:shared-status") != wxNOT_FOUND)) { /* fixme: disco info response would satisfy this condition */

      /* extract and save the shared status substring recieved from the server */

     if (packet.Find("type=\"error\"") == wxNOT_FOUND) {
      
      wxString extractSS = (packet.AfterFirst('>')).AfterFirst('>');

      assert(extractSS.Len());

      extractSS.Replace("sta:", "");

      c->j_ss = new wxString("<iq type='set' to='" +
                             *((wxString *)(c->m_user)) + 
                             "' id='ss-2'><query xmlns='google:shared-status' version='2'>" +
                             extractSS);
     } else {
        j_notify(wxString("ERROR: google:shared-status"), packet);
     }

     process_status(c);
     
     show_j_panel();

   } else if ((packet.Find("type=\"result\"") != wxNOT_FOUND) &&
              (packet.Find("google:mail:notify") != wxNOT_FOUND)) {

      if (packet.Find("total-matched=\"0\"") == wxNOT_FOUND) { 

        int index = packet.Find("total-matched=\"") + strlen("total-matched=\"");

        j_mail->SetLabel((packet.SubString(index, index+5)).BeforeFirst('"'));

        j_mail->GetLabel().ToLong(&(c->j_nMail));
        
        if (j_sizer->IsShown(j_szTB)) { 
          j_mail->Show();
        }

      } else {

        j_mail->SetLabel(wxEmptyString);

        j_mail->Hide();
      }

      wxString tip;
      long count;

      j_mail->GetLabel().ToLong(&count);

      count = count > 5 ? 5 : count;

      for (int i=1; i<=count; i++) {
        wxString tofind("<subject>"), sub, snip;
        xml_element(packet, tofind, &sub);
        tofind = "<snippet>";
        xml_element(packet, tofind, &snip);
        tip.Append("[" + sub + "]\n");
        tip.Append(snip + " ...");
        tip.Append("\n\n");
        packet.Replace("<subject>", "done", false);
        packet.Replace("<snippet>", "done", false);
      }

      j_mail->SetToolTip(tip);

      j_szTool->Layout();

      this->RefreshRect(j_mail->GetRect());

   } else if ((packet.Find("type=\"result\"") != wxNOT_FOUND) &&
              (packet.Find("vcard-temp") != wxNOT_FOUND)) {

     logit_a ("processing vCard-Based avatars..\n");

     wxString id, tofind("id=");

     xml_element(packet, tofind, &id, true);

     assert(id);

     /* Get the full name */

     wxString fullname;

     tofind = "<FN>";

     if (!xml_element(packet, tofind, &fullname)) {
       logit_a ("FN> not found\n");
     }

     /* get the image */

     wxImage  *avatar = NULL;
     wxString image;

     tofind = "<BINVAL>";

     if (xml_element(packet, tofind, &image)) {

       /* 
        * for Google wxBase64DecodeMode_Strict works 
        * facebook however requires wxBase64DecodeMode_Relaxed
        */

       wxMemoryBuffer mBuf = wxBase64Decode(image, wxBase64DecodeMode_Relaxed);

       wxInputStream *pis = new wxMemoryInputStream(mBuf.GetData(), mBuf.GetDataLen());

       avatar = new wxImage(*pis, wxBITMAP_TYPE_ANY, -1);

     } else {

       logit_a ("BINVAL> not found\n");
     }

     IMBuddy *buddy = (IMBuddy *) find_node(c, id);

     if (!id.Cmp("vcard_self")) {

       logit_a ("processing our own vcard\n");
       
       if (c->j_vcard) {       
         delete (wxString *)c->j_vcard , c->j_vcard = 0;
       }

       c->j_vcard = new wxString(packet);

       update_title_bar(0, c); 
       
       process_status(c);

       j_contacts->Refresh();
       
       if (avatar) {
         delete avatar, avatar = 0;
       }

     } else { /* contact's vcard */

       logit_a ("processing contact's v-card buddy %x\n", buddy);

       if (buddy) {

         if (avatar) {

           *avatar = avatar->Scale(48, 48, wxIMAGE_QUALITY_HIGH);

           buddy->b_avatar = avatar;

           j_contacts->SetItemImage(buddy->b_i, 0, avatar);

         } else if (fullname.Len()) {

           /* 
            * The contact does not have a profile pic
            * swith to old pics based on the status 
            * but only when fullname is there..
            */

           if (buddy->b_status == status_invalid) {

             j_contacts->SetItemImage(buddy->b_i, invited_xpm, 0);

           } else if (buddy->b_status == status_offline) {

             j_contacts->SetItemImage(buddy->b_i, offline_xpm, 0);

           } else if (buddy->b_status == status_available) {

             j_contacts->SetItemImage(buddy->b_i, online_xpm, 0);

           } else if (buddy->b_status == status_away) {

             j_contacts->SetItemImage(buddy->b_i, away_xpm, 0);

           } else if (buddy->b_status == status_busy) {

             j_contacts->SetItemImage(buddy->b_i, busy_xpm, 0);

           } else {

             assert(0);
           }

         } /* else The contact does not have a profile pic */

         if (fullname.Len()) {

           j_contacts->SetItemText(buddy->b_i, fullname, 0);

           if (buddy->b_name) {

             buddy->b_name->Clear();

           } else {

             buddy->b_name = new wxString();
           }

           *(buddy->b_name) = fullname;        
         }

         if (buddy->b_vcard) {

           delete buddy->b_vcard;
         }

         buddy->b_vcard = new wxString(packet);
       } /* if buddy */
       
       j_contacts->szWrap->Layout();

       j_contacts->Refresh();
      }

   } else if (packet.Find("new-mail xmlns=\"google:mail:notify\"") != wxNOT_FOUND) {

      logit_a ("processing google:mail:notify ..\n");

      wxString mails = wxString("<iq type=\"get\" id=\"25\"><query xmlns=\"google:mail:notify\"/></iq>");

      j_send (c, mails);

   } else if ((packet.Find("type=\"result\"") != wxNOT_FOUND) && 
              (packet.Find("jabber:iq:roster") != wxNOT_FOUND)) {

      logit_a ("processing jabber:iq:roster result..\n");

      update_roster(c, packet);

      /* for facebook, bring up the jabber panel now */

      if (((wxString *)(c->j_jid))->Contains("chat.facebook.com")) {

        xmpp_status(c, "chat", false);
        
        show_j_panel();
      }

   } else if ((packet.Find("type=\"set\"") != wxNOT_FOUND)     && 
              (packet.Find("jabber:iq:roster") != wxNOT_FOUND) &&
              ((packet.Find("subscription=\"both\"") != wxNOT_FOUND) || (packet.Find("ask=\"subscribe\"") != wxNOT_FOUND))) {

     wxString id, tofind("jid=");

     xml_element(packet, tofind, &id, true);

     assert(id.Len());

     IMBuddy *buddy = (IMBuddy *) find_node(c, id);
     
     if (buddy) {

       if ((packet.Find("subscription=\"both\"") != wxNOT_FOUND) &&
           (buddy->b_status == status_invalid)) {

         j_contacts->SetItemImage(buddy->b_i, offline_xpm, 0);
       }

       return;
     }

     buddy = (IMBuddy *) add_node(c, id);

     tofind = "name=";

     xml_element(packet, tofind, buddy->b_name, true);

     if (!j_head) {

       j_head = j_tail = buddy;  /* workflow: create a new gmail account and add it from a different account */

     } else {

       j_tail->next = buddy;
       j_tail = buddy;
     }

     wxString tip = *(buddy->b_id) + wxString("\n"); 

     buddy->b_i = j_contacts->InsertItem(buddy->b_name ?  *(buddy->b_name) : *(buddy->b_id), 
                                         buddy->b_name ? *(buddy->b_id) : wxString(_("Offline")), tip, 
                                         packet.Find("ask=\"subscribe\"") != wxNOT_FOUND ? invited_xpm : offline_xpm);

     j_contacts->SetItemData(buddy->b_i, buddy);

     j_contacts->szWrap->Layout();

     j_contacts->Refresh();

   } else if ((packet.Find("</stream:stream>") != wxNOT_FOUND)) {

     logit_a ("end of xml stream recieved..\n");

   } else if (packet.Find("type=\"error\"") != wxNOT_FOUND) { 

      j_notify(wxString("ERROR"), packet);

   } else {

     logit_a ("skipping ..\n");
   }

   logit_a ("%s(%d): <-\n", __func__, GetCurrentThreadId());
 }

 void
  Jabber::end_stream(connection *c)
 {
   wxString eof = wxString("</stream>");
   assert (c);
   j_send (c, eof);
 }

 void *
  Jabber::find_node(connection *c, wxString& id)
 {
   IMBuddy *buddy = j_head;
   
   while (buddy) {

    assert (buddy->b_id);

    if (!((wxString *)buddy->b_id)->CmpNoCase(id) &&
        c == buddy->b_c) {
      break;
    }

    buddy = buddy->next;
  } 

  return (void *)buddy;
 }

 void *
  Jabber::add_node(connection *c, wxString& id, const char **status_xmp)
 {
   wxString tip;

   IMBuddy *buddy = (IMBuddy *) calloc(1, sizeof(IMBuddy));

   if (!j_head) { /* make if the other way round */

     j_head = j_tail = buddy;

   } else {

     j_tail->next = buddy;
     j_tail = buddy;
   }

   buddy->b_id = new wxString(id);

   buddy->b_resource = new wxString();

   buddy->b_chat = new wxString();

   buddy->b_punchline = new wxString();

   buddy->b_c = c;
   
   tip.Append(*(buddy->b_id) + wxString("\n"));

   buddy->b_i = j_contacts->InsertItem(buddy->b_name ?  *(buddy->b_name) : *(buddy->b_id), 
                                       buddy->b_name ? *(buddy->b_id) : wxString(_("Offline")),
                                       tip,
                                       status_xmp);

   j_contacts->SetItemData (buddy->b_i, buddy);

   return (void *)buddy;
 }

 void
  Jabber::process_status(connection *c)
 {
   if (!c->j_ss) {
     xmpp_status(c, "default", false);
     return;
   }

   if (((wxString *)(c->j_ss))->Find("invisible value=\"true\"") != wxNOT_FOUND) {
 
     logit_a ("%s(%d): user is invisible\n", __func__, GetCurrentThreadId());

     xmpp_status(c, "default", true);
 
   } else if ((((wxString *)(c->j_ss))->Find("<show>default") != wxNOT_FOUND) ||
              (((wxString *)(c->j_ss))->Find("<show/>") != wxNOT_FOUND)       ||
              (((wxString *)(c->j_ss))->Find("<show") == wxNOT_FOUND)) {

     logit_a ("%s(%d): default, empty or no <show = online\n", __func__, GetCurrentThreadId());

     xmpp_status(c, "default");

   } else if (((wxString *)(c->j_ss))->Find("<show>dnd") != wxNOT_FOUND) {
 
     logit_a ("%s(%d): user is dnd in shared status\n", __func__, GetCurrentThreadId());

     xmpp_status(c, "dnd");

   } else {

     logit_a ("%s(%d): away ..\n", __func__, GetCurrentThreadId());

     xmpp_status(c, "away");
   }

   /* 
    * status-list element contains <status> hence we dont
    * "Find" <status> instead we find <status this would
    * have a message or could potentially be an empty element
    */

   wxString punchline;

   wxString tofind("<status>");

   xml_element(*((wxString *)(c->j_ss)), tofind, &punchline);

   if (!punchline.Len()) {
     punchline = _("share a message");
   }

   xml_entity_inbound(punchline.Trim(false));

   j_punchline->SetLabel(punchline.Trim(false));

   j_punchline->SetToolTip(punchline.Trim(false));

   j_punchline->Refresh();
 }

 void
  NP::xmpp_state_machine (wxCommandEvent& e)
 {
   wxMutexLocker lock(*m_mutex);

   connection *c = (connection *) e.GetClientData();
   
   logit_a ("%s(%d): state %d p2 %p c %p ->\n",
            __func__,
            GetCurrentThreadId(),
            c->m_state, p2, c);

   switch (c->m_state)
   {
     case 1:
     {
       n_status->SetLabel(_("Connected to ") + wxString(c->m_host));

       szTop->Layout();

       wxString hello = wxString("<stream:stream to=\"") +
                                 (strstr(c->m_host, "google") ? wxString("gmail.com") : wxString("chat.facebook.com")) +
                                 wxString("\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">");

       ((Jabber *)p2)->j_send (c, hello);

       c->m_state++;

       break;
     }

     case 2:
     {
       wxString *response = ((Jabber *)p2)->j_recv(c);

       while (response->Find("urn:ietf:params:xml:ns:xmpp-tls") == wxNOT_FOUND) {

         delete response, response = 0;

         response = ((Jabber *)p2)->j_recv(c);
       }

       delete response;

       wxString starttls = wxString("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");

       ((Jabber *)p2)->j_send (c, starttls);

       c->m_state++;

       break;
     }

     case 3:
     {
       n_status->SetLabel(_("Securing connection"));
       
       szTop->Layout();

       delete (((Jabber *)p2)->j_recv(c));

       SSL_CTX *ctx;
 
       ctx = SSL_CTX_new(TLSv1_method());

       (c->m_sec).ssl = SSL_new(ctx);

       BIO *sbio = BIO_new_socket(c->m_sd, BIO_NOCLOSE);

       SSL_set_bio((c->m_sec).ssl, sbio, sbio);

       int rc;

       if ((rc = SSL_connect((c->m_sec).ssl)) <= 0) {

         logit_a ("%s: TLS handshake failed c %x err %d\n", __func__, c, SSL_get_error((c->m_sec).ssl, rc));

         n_status->SetLabel(_("SSL handshake failed"));
         
         szTop->Layout();

         assert(false);

       } else {

         (c->m_sec).flags |= FLAG_SECURE;

         logit_a ("%s: TLS handshake successfull c %p\n", __func__, c);
         
         n_status->SetLabel(_("Security handshake successful"));
         
         szTop->Layout();

         wxString hello = wxString("<stream:stream to=\"") +
                                   (strstr(c->m_host, "google") ? wxString("gmail.com") : wxString("chat.facebook.com")) +
                                   wxString("\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">");

         ((Jabber *)p2)->j_send (c, hello);

         c->m_state++;
       }
       break;
     }

     case 4:
     {
        wxString *response =  ((Jabber *)p2)->j_recv(c);

        if (!((Jabber *)p2)->check_response (response)) {
           break;
        }

        Gsasl_session *session;
        Gsasl         *ctx = NULL;
        char          *oBuf = NULL;
        int            rc;

        n_status->SetLabel(_("Authenticating user"));
         
        szTop->Layout();
 
        if (response->Find("<mechanism>PLAIN</mechanism>") != wxNOT_FOUND) {

         rc = gsasl_init (&ctx);

         if (rc != GSASL_OK) {
           logit_a ("%s(%d): SASL initialization failure (%d): %s\n", __func__, GetCurrentThreadId(), rc, gsasl_strerror(rc));
           break;
         }
       
         if ((rc = gsasl_client_start (ctx, "PLAIN", &session)) != GSASL_OK) {
           logit_a ("%s(%d): Cannot initialize client (%d): %s\n", __func__, GetCurrentThreadId(), rc, gsasl_strerror(rc));
           break;
         }
         
         gsasl_property_set (session, GSASL_AUTHID, ((wxString *)c->m_user)->mb_str());
         gsasl_property_set (session, GSASL_PASSWORD, ((wxString *)c->m_password)->mb_str());

         /* For mechanisms that require the client to send data 
          * first the client may optionally send its first response 
          * as part of the mechanism selection: 
          */

         wxString answer("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>");
 
         rc = gsasl_step64(session, NULL, &oBuf);
 
         answer.Append(wxString(oBuf));
         answer.Append(wxString("=</auth>"));

         ((Jabber *)p2)->j_send (c, answer);

		 free(oBuf);  
         
         gsasl_finish (session);
         gsasl_done (ctx);          
         
         c->m_state++;

        } else if (response->Find("DIGEST-MD5") != wxNOT_FOUND) {

          wxString auth = wxString("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");

          ((Jabber *)p2)->j_send (c, auth);

        } else if (response->Find("<challenge ") != wxNOT_FOUND) { 

         rc = gsasl_init (&ctx);

         if (rc != GSASL_OK) {
           logit_a ("\n%s(%d): SASL initialization failure (%d): %s", __func__, GetCurrentThreadId(), rc, gsasl_strerror(rc));
           break;
         }       
       
         if ((rc = gsasl_client_start (ctx, "DIGEST-MD5", &session)) != GSASL_OK) {
           logit_a ("\n%s(%d): Cannot initialize client (%d): %s", __func__, GetCurrentThreadId(), rc, gsasl_strerror(rc));
           break;
         }

         gsasl_property_set (session, GSASL_AUTHID, ((wxString *)c->m_user)->mb_str());
         gsasl_property_set (session, GSASL_PASSWORD, ((wxString *)c->m_password)->mb_str());
         gsasl_property_set (session, GSASL_SERVICE, "xmpp");
         gsasl_property_set (session, GSASL_HOSTNAME, "chat.facebook.com"); 

         do
         {
           wxString answer("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>");
           wxString challenge = response->AfterFirst('>').BeforeFirst('<');

           rc = gsasl_step64(session, challenge.c_str(), &oBuf);

           if (rc == GSASL_NEEDS_MORE) {
              answer.Append(wxString(oBuf));
              answer.Append(wxString("</response>")); 
              ((Jabber *)p2)->j_send (c, answer);
			  free(oBuf);

              delete response, response = NULL;

              response =  ((Jabber *)p2)->j_recv(c);

              if (((Jabber *)p2)->check_response (response) &&
                  response->StartsWith("<failure ")) {

                n_status->SetLabel(_("Authentication failed !"));

                szTop->Layout();

                ((Jabber *)p2)->end_stream(c);
              }
              
           } else {
              logit_a ("\nDIGEST-MD5 gsasl_step64 returned (%d) --> %s", rc, gsasl_strerror(rc));
           }

         } while (rc == GSASL_NEEDS_MORE);
	 
         if (rc != GSASL_OK) {

           logit_a ("\nDIGEST-MD5 authentication error (%d) --> %s\n", rc, gsasl_strerror(rc));
           break;

         } else {

           wxString final = wxString("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
		   ((Jabber *)p2)->j_send (c, final);
           c->m_state++;
		 }

         gsasl_finish (session);
         gsasl_done (ctx); 
        }
       
       delete response;       
       
       break;
     }

     case 5:
     {
       wxString *response = ((Jabber *)p2)->j_recv(c);
      
       if (!response->StartsWith("<failure ")) {

         n_status->SetLabel(_("Authentication successful"));
         
         szTop->Layout();

         wxString hello = wxString("<stream:stream to=\"") +
                                   (strstr(c->m_host, "google") ? wxString("gmail.com") : wxString("chat.facebook.com")) +
                                   wxString("\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">");

         ((Jabber *)p2)->j_send (c, hello);

         put_cfg("[User]", ((wxString *)c->m_user)->BeforeFirst('@'), false);

       } else {

         n_status->SetLabel(_("Authentication failed !"));

         szTop->Layout();
       }
 
       delete response;

       c->m_state++;

       break;
     }

     case 6:
     {
       wxString *response =  ((Jabber *)p2)->j_recv(c);
 
       if (((Jabber *)p2)->check_response (response)) {

         if (response->Find("ns:xmpp-bind") != wxNOT_FOUND) {

           n_status->SetLabel(_("Binding resource"));

           szTop->Layout();

           wxString jBind = wxString("<iq type='set' id='np_bind'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>NP.</resource></bind></iq>");

           ((Jabber *)p2)->j_send (c, jBind);

           c->m_state++;

         } else if ((response->Find("</stream:stream>") != wxNOT_FOUND)) {

           /* this would be true in case login fails in case 5 */

           ((Jabber *)p2)->process_xmpp_packet(c, *response);
         }
       }

       delete response;

       break;
     }
 
     case 7:
     {
       wxString *response = ((Jabber *)p2)->j_recv(c);

       if (((Jabber *)p2)->check_response (response)) {

         if ((response->Find("type=\"result\"") != wxNOT_FOUND) &&
             (response->Find("np_bind") != wxNOT_FOUND)) {

           wxStringTokenizer tokenizer(*response, "@");

           wxString before = tokenizer.GetNextToken();

           wxString after = tokenizer.GetNextToken();

           c->j_jid = new wxString(before.AfterLast('>') + "@" + after.BeforeFirst('<'));

		   wxString jSession = wxString("<iq type='set' id='np_session'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>");

           ((Jabber *)p2)->j_send (c, jSession);

		   c->m_state++;
         }
       }
 
       delete response;

       break;
      }

	  case 8:
	  {
        wxString *response = ((Jabber *)p2)->j_recv(c);

        if ((response->Find("type=\"result\"") != wxNOT_FOUND) &&
            (response->Find("np_session") != wxNOT_FOUND)) {

          /* Get the roster with extended attributes set */
         
          n_status->SetLabel(_("Retreving contact list"));
 
          szTop->Layout();

          wxString getroster = wxString("<iq from='" +
                                        *((wxString *)c->j_jid) + 
                                        "' id='np-roster' type='get'><query xmlns='jabber:iq:roster'" + 
										(strstr(c->m_host, "google") ? wxString("xmlns:gr='google:roster' gr:ext='2'") : "") +
                                        wxString("/></iq>"));

          ((Jabber *)p2)->j_send (c, getroster);

          c->m_state++;
		}

        delete response;

        break;
	  }
	  
      case 9:
      {
        wxString *response = ((Jabber *)p2)->j_recv(c);

        if (((Jabber *)p2)->check_response (response)) {
      
          ((Jabber *)p2)->process_xmpp_packet(c, *response);
          
		  if (!((wxString *)c->j_jid)->Contains("chat.facebook.com")) {            

            /* shared status */
            wxString ss_req = wxString("<iq type='get' to='" + *((wxString *)c->m_user) + "' id='np_ss' ><query xmlns='google:shared-status' version='2'/></iq>");
            ((Jabber *)p2)->j_send (c, ss_req);

            /* check mails */
            wxString gmail_req = wxString("<iq type=\"get\" id=\"np_mail\"><query xmlns=\"google:mail:notify\"/></iq>");
            ((Jabber *)p2)->j_send (c, gmail_req);

            /* STUN and TURN server retrieval */            
            wxString server_req = wxString("<iq type='get' from='" +  *((wxString *)c->j_jid) + "' to='" + *((wxString *)c->m_user) + "' id='np_server'><query xmlns='google:jingleinfo'/></iq>");
            ((Jabber *)p2)->j_send (c, server_req);
          }

          /* self vCard retrieval request */
          wxString vself_req = wxString("<iq from='" +  *((wxString *)c->j_jid) + "' id='vcard_self' type='get'><vCard xmlns='vcard-temp'/></iq>");
          ((Jabber *)p2)->j_send (c, vself_req);
          
          c->m_state++;
        }

        break;
      }

      default:
      {
        wxString *response = ((Jabber *)p2)->j_recv(c);

        if (((Jabber *)p2)->check_response (response)) {
          ((Jabber *)p2)->process_xmpp_packet(c, *response);
        }
 
        break;
      }
   }

   if (lock.IsOk()) {

     m_condition->Broadcast();

   } else {

     logit_a ("%s(%d): lock.IsOk failed\n", __func__, GetCurrentThreadId());
   }

   logit_a ("%s(%d): <-\n", __func__, GetCurrentThreadId());
 }

 void
  Jabber::OnKillFocusStatusText(wxFocusEvent& e)
 {
   bool bStatus, bSend;
   wxString old, ss;
   connection *c = (connection *) j_status->m_data;

   assert (c);
   
   bStatus = bSend = false;

   Freeze();

   wxString status = j_text->GetValue();

   if (!status.Cmp("share a message")) {
     status = "";
   }

   j_szTool->Show(j_text, false);

   j_szTool->Show(j_punchline, true);

   if (!c->j_ss) {
     goto _end;
   }

   ss = (((wxString *)(c->j_ss))->AfterFirst('>')).AfterFirst('>');

   if (!ss.StartsWith("<invisible")) {

     /* this assumes that <status comes before <show element */

     if (ss.StartsWith("<status>")) {
 
      /* <status> is present; check for empty element <status><status/> */

       if (ss[strlen("<status>")] != '<') {

         wxString tofind("<status>");

         xml_element(ss, tofind, &old);
      
         assert(old.Len());

         bStatus = true;
       }
     }
   } else { 

    /* ss starts with <invisible none of status, show or list are present */
   }

   if (status.Len()) {

     j_punchline->SetLabel(status);

     if (bStatus && old.Len()) {

       ss.Replace("<status>" + old + "</status>", "<status>" + status + "</status>", false);

     } else {

       /* first unconditionally replace <status/>  */
       /* and then insert <status>....</status>   */

       ss.Replace("<status/>", "", false);

       ss.Prepend("<status>" + status + "</status>");
     }

     bSend = true;

   } else { 

     /* user did not give us a new message OR intends to clear the old message */

     if (bStatus && old.Len()) {

       ss.Replace("<status>" + old + "</status>", "<status/>", false);

       bSend = true;
     }

     j_punchline->SetLabel(_("share a message"));
   }

   if (bSend) {

     ss.Prepend("<iq type='set' to='" +
                *((wxString *)(c->m_user)) +
                "' id='ss-2'><query xmlns='google:shared-status' version='2'>");

     j_send(c, ss); 
   }

   _end:
   
   j_szTool->Layout();

   j_sizer->Layout();

   Thaw();

   e.Skip(); 
 }

 bool
  Jabber::j_send (connection *c, wxString &message)
 {
   bool   fRet = false;
   char  *buf  = NULL;
   int    len = message.Len(), nWritten = 0;
   
   logit_a ("\n[%s(%d): c %p len %d] ", __func__, GetCurrentThreadId(), c, len);
     
   buf = (char *) calloc (1, len * sizeof(wchar_t));

   if (buf) {

     wcstombs(buf, message, len * sizeof(wchar_t));

     OutputDebugStringW (message.wc_str());

     nWritten = c->send_data (buf, strlen(buf));

     free(buf);

   } else {
     logit_a ("\n%s(%d): failed to allocate send buffer\n", __func__, GetCurrentThreadId());    
     assert(false);
   }

   logit_a (" [<- %s(%d): c %p bytes written %d]\n", __func__, GetCurrentThreadId(), c, nWritten);

   return (fRet);   
 }

 wxString *
  Jabber::j_recv (connection *c)
 {
   wxString  *xml = NULL;
   int        r = 0, nBytes = 0;

   logit_a ("\n[%s: c %p] ", __func__, c);

   xml = new wxString();

   do 
   {
     r = c->recv_data(rBuf, MAX_RBUF, 0);

     if (r <= 0) {

       logit_a (" %s: ERROR r %d\n", __func__, r);

       j_notify(((wxString *)(c->m_user))->BeforeFirst('@')+ wxString("@")+wxString(c->m_host), wxString("Session closed")); 

       IMBuddy *buddy = j_head;

       Freeze();

       while (buddy) {

         if (buddy->b_c == c) {
           j_contacts->HideItem((buddy->b_isShown = false, buddy->b_i));  
         }

         buddy = buddy->next;
       }

       j_contacts->szWrap->Layout();
      
       Thaw();

       memset(c, 0, sizeof(connection));
       
       delete xml, xml = 0;

       goto _end;

     } else {

       nBytes += r;
     }

     xml->Append(rBuf, r);

   } while (((*xml)[0] == '<') && !xml_feed(xml));

   _end:

   if (r > 0) {
     OutputDebugStringW (xml->wc_str());
   }

   /* logit_a ("[%s: nBytes %d c %p<-]\n", __func__, nBytes, c);*/
 
   return xml;
 }

 bool
  Jabber::check_response (wxString *response)
 {
   int i;
 
   if (!response) {
   
      logit_a ("%s(%d): null response packet\n", __func__, GetCurrentThreadId());
      
      for (i = 0; i < MAX_SESSIONS; i++) {
        if (c_list[i].m_state){
          logit_a ("%s(%d): c_list[%d].m_state not NULL\n", __func__, GetCurrentThreadId(), i);
          update_title_bar(0, c_list+i);
          process_status(c_list + i);
          break;
        }
      }

      if (i == MAX_SESSIONS) {
      
        wxGetApp().m_frame->remove_protocol_panel();

      } else {
      
      }

      return (false);

   } else {

      return (true);
   }
 }

 /*
  *  in:   arbitraty number of xml stanzas as a stream
  *  out:  recursively checks for the completeness of all stanzas
  */

 bool
  xml_feed (wxString *xml)
 {
   bool fRet = true;

   if (!xml) {
     return (false);
   }

   wxString rest;

   if (xml->AfterFirst(' ').Find("<iq ") != wxNOT_FOUND       ||
       xml->AfterFirst(' ').Find("<presence ") != wxNOT_FOUND ||
       xml->AfterFirst(' ').Find("<message ") != wxNOT_FOUND  ||
       xml->StartsWith("<?xml version")                       ||  /*  XMPP core, section 4.1 (facebook throws this) */
       xml->StartsWith("<stream:stream")                      ||
       xml->StartsWith("<stream:features")) {

        int index = wxNOT_FOUND;

        if ((index = xml->AfterFirst(' ').Find("<iq ")) != wxNOT_FOUND) {

          logit_a ("%s(%d): recursing for <iq \n", __func__, GetCurrentThreadId());

        } else if ((index = xml->AfterFirst(' ').Find("<presence ")) != wxNOT_FOUND) {

          logit_a ("%s(%d): recursing for <presence \n", __func__, GetCurrentThreadId());

        } else if ((index = xml->AfterFirst(' ').Find("<message ")) != wxNOT_FOUND) {        

          logit_a ("%s(%d): recursing for <message \n", __func__, GetCurrentThreadId());

        } else if (xml->StartsWith("<stream:stream")) { 

          index = xml->AfterFirst(' ').Find('>') + 1;

        }  else if (xml->StartsWith("<stream:features")) {

          index = xml->Find("</stream:features") + strlen ("</stream:features");

        } else if (xml->StartsWith("<?xml version")) {

          index = xml->Find('>') + 1;
        }


      if ((index != wxNOT_FOUND) && ((index+1) != xml->Len())) {

        rest = xml->AfterFirst(' ').Mid(index);

        if (rest.Len()) {
          xml_feed (&rest);
        }

      } else {

        fRet = true;
      }

   } else {

     /* no more trailing stanzas; check if the last one is okay */

     wxString terminator("</");

     terminator.Append(xml->BeforeFirst(' ').AfterFirst('<'));

     terminator.Append('>');

     xml->Trim();

     if (((xml->Find('<', true) == 0) && xml->EndsWith("/>")) || (xml->EndsWith(terminator))) {

       /* 
        * it is a complete stanza if the first opening bracket from 
        * the end is at index 0 and the message ends with /> OR if 
        * the message ends with the terminator.
        */

        fRet = true;

     } else {

        fRet = false;
     }
   }

   return (fRet);
 }

 /* Jabber panel */

 Jabber::Jabber(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
        : wxPanel(parent, id, pos, size, style)
 {
   j_head        = 0;
   j_fPlus       = 0;
   j_hover       = 0;
   j_friendReq   = 0;
   j_hideoffline = 1;
   g_alert       = 0;

   this->SetBackgroundColour(*wxWHITE);

   j_sizer = new wxBoxSizer(wxVERTICAL);
  
   j_szTB = new wxBoxSizer(wxVERTICAL);

   j_Row1 = new wxBoxSizer(wxHORIZONTAL);

   j_Row1->SetMinSize(96+4+4+4, 96+4+4+4);

   j_avatar = new QBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0|wxTRANSPARENT_WINDOW, "bitmap_avatar");
   j_Row1->Add(j_avatar, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); 
   j_avatar->Hide();

   j_Row1->Add(0, 0, 1, wxALIGN_CENTER_VERTICAL, 5); 

   j_Row2 = new wxBoxSizer(wxVERTICAL);

   j_fn = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
   j_fn->Wrap(-1);
   j_fn->SetFont(wxFont(wxNORMAL_FONT->GetPointSize()+1, 70, 90, 91, false, wxT("Verdana")));

   j_Row2->Add(j_fn, 0, wxRIGHT|wxALIGN_RIGHT, 5);

   j_fid = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
   j_fid->Wrap(-1);
   j_fid->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 91, false, wxT("Verdana")));
   j_fid->SetForegroundColour(wxColour(34,34,255));

   j_Row2->Add(j_fid, 0, wxRIGHT|wxALIGN_RIGHT, 5);

   j_Row2->Add(0, 0, 1, wxEXPAND, 5);

   j_Row1->Add(j_Row2, 0, wxALIGN_RIGHT|wxEXPAND, 5);

   j_szTB->Add(j_Row1, 0, wxEXPAND, 5);

   j_szTool = new wxBoxSizer(wxHORIZONTAL);

   j_punchline = new QTab(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW, _("share a message"), NULL, "punchline", true );

   j_punchline->m_highlight = new wxPen( wxColour(160, 160, 160) );
   j_punchline->m_textcolour = wxColour(149, 128, 170);
   j_punchline->SetToolTip(_("share a message"));
   j_punchline->m_round = false;

   j_szTool->Add(j_punchline, 0, wxALL, 5);

   j_text = new wxRichTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, 75), 0|wxSIMPLE_BORDER|wxTE_AUTO_URL|wxWANTS_CHARS|wxTE_PROCESS_ENTER|wxVSCROLL);
   j_text->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxT("Verdana")));
   j_text->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(Jabber::OnKillFocusStatusText), NULL, this);

   j_szTool->Add(j_text, 0, wxALL|wxEXPAND, 5);

   j_text->Hide();

   j_szTool->Add(0, 0, 1, wxEXPAND, 5);

   j_fPlus = new QTab(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW, wxEmptyString, star_xpm, "fPlus", true);

   j_fPlus->m_highlight = new wxPen(wxColour(160, 160, 160));
   j_fPlus->m_textcolour = wxColour(63, 78, 204);
   j_fPlus->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 91, false, wxT("Verdana")));
   j_fPlus->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(Jabber::starOnLeftDown), NULL, this);

   j_szTool->Add(j_fPlus, 0, wxALL, 5);

   j_mail = new QTab(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW, wxEmptyString , mail_xpm, "mail", true);
   j_mail->GetToolTip()->SetAutoPop(20000);
   j_mail->m_highlight = new wxPen( wxColour(160, 160, 160) );
   j_mail->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 91, false, wxT("Verdana")));
   j_mail->m_textcolour = wxColour(63, 78, 204);

   j_mail->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(Jabber::mailOnLeftDown), NULL, this);

   j_szTool->Add(j_mail, 0, wxALL, 5);

   j_status = new QTab(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW, wxEmptyString, 0, "status", false);
   j_status->m_highlight = new wxPen( wxColour(160, 160, 160));
   j_status->m_round = false;
   j_status->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 91, false, wxT("Verdana")));
   j_status->SetToolTip(_("Change your status"));

   j_szTool->Add(j_status, 0, wxALL, 5);   

   j_mail->Hide();
   j_fPlus->Hide();

   j_szTB->Add(j_szTool, 0, wxEXPAND, 5);

   j_szBar = new wxBoxSizer(wxHORIZONTAL);
   
   j_szBar->Add(0, 0, 1, wxEXPAND, 5);
 
   j_searchEdit = new QEdit(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,-1), 0|wxSIMPLE_BORDER|wxTE_PROCESS_ENTER, wxDefaultValidator, wxTextCtrlNameStr);
   j_searchEdit->SetFont( wxFont( wxNORMAL_FONT->GetPointSize()+2, 70, 90, 90, false, wxT("Verdana")) );

   //j_searchEdit->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler(Jabber::m_emailOnKillFocus), NULL, this );
   //j_searchEdit->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(Jabber::searchOnText), NULL, this );

   j_szBar->Add(j_searchEdit, 0, wxLEFT|wxRIGHT|wxTOP|wxALIGN_CENTER_VERTICAL, 5 );

   j_searchEdit->Hide(); 
 
   j_search = new QTab(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW, _("Search"), search_xpm, "search", true );

   j_search->m_highlight = new wxPen( wxColour(160, 160, 160) );
   j_search->m_textcolour = wxColour(149, 128, 170);
   j_search->SetToolTip(_("search a contact"));

   j_szBar->Add(j_search, 0, wxALL, 5); 

   j_contacts = new Qlist(this);
   j_contacts->SetName("J");
   j_contacts->SetBackgroundColour(*wxWHITE);
   
   j_sizer->Add(j_szTB, 0, wxEXPAND, 5);
   j_sizer->Add(j_szBar, 0, wxEXPAND, 5);   
   j_sizer->Add(j_contacts, 0, wxEXPAND, 5);

   this->SetSizer(j_sizer);
   
   this->Layout();
   
   this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(Jabber::OnLeftDown), NULL, this);

   rBuf = (char *) malloc (MAX_RBUF);
 }

 Jabber::~Jabber() 
 {
   int x, y;
      
   if (!wxGetApp().m_frame->szTop->IsShown(wxGetApp().m_frame->szQC))
   {
     wxGetApp().m_frame->put_cfg("[Width]", wxString::Format(wxT("%i"), wxGetApp().m_frame->GetSize().GetWidth()));
     wxGetApp().m_frame->put_cfg("[Height]", wxString::Format(wxT("%i"), wxGetApp().m_frame->GetSize().GetHeight()));
 
     wxGetApp().m_frame->GetScreenPosition(&x, &y);

     wxGetApp().m_frame->put_cfg("[x]", wxString::Format(wxT("%i"), x));
     wxGetApp().m_frame->put_cfg("[y]", wxString::Format(wxT("%i"), y));
   }
   
   free (rBuf);
 }

 bool
  Jabber::xml_element(wxString& stanza, wxString& element, wxString *val, bool isAttribute)
 {
   bool fRet = false;

   int index = stanza.Find(element);

   if ((index == wxNOT_FOUND) ||
       (stanza[index+element.Len()] == '<')) {
     goto _end;
   }

   index += element.Len();

   if (isAttribute) {

     index++;

     while ((stanza[index] != '\'') &&
            (stanza[index] != '"') ) {
       val->Append(stanza[index++]);
     }

   } else {
   
     while (stanza[index] != '<') {
       val->Append(stanza[index++]);
     }
   }

   assert(val->Len());

   fRet = true;

   _end:

   return (fRet);
 }

 void
  Jabber::j_notify(wxString title, wxString message)
 {
   if (message.Len()) {
     (new TaskBarBaloon(title, message))->showBaloon(4000);
   }
 }
 
 void
  Jabber::show_j_panel()
  {
    wxString szX, szY, szW, szH;
    long x, y, w, h;
 
    wxGetApp().m_frame->n_status->SetLabel("");   
     
    if (this->IsShown()) {
      return;
    }
    
    wxGetApp().m_frame->get_cfg("[x]", &szX);   
    wxGetApp().m_frame->get_cfg("[y]", &szY); 
    wxGetApp().m_frame->get_cfg("[Width]", &szW); 
    wxGetApp().m_frame->get_cfg("[Height]", &szH);

    x = y = w = h = 0;

    wxGetApp().m_frame->szTop->Hide(wxGetApp().m_frame->szQC, true);

    this->Show();
    
    if (!szW.ToLong(&w) || 
        !szH.ToLong(&h) || 
        !w              ||
        !h) {
       w = 416;
       h = 663;
    }

    wxGetApp().m_frame->SetSize(w, h);
      
    wxSize dsz = ::wxGetDisplaySize();

    if (!szX.ToLong(&x) ||
        !szY.ToLong(&y) ||
        !x              ||
        !y) {
      x = dsz.GetWidth()-550;
      y = 50;
    }

    wxGetApp().m_frame->Move(x, y);

    wxGetApp().m_frame->szTop->Layout();

    wxGetApp().m_frame->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(NP::OnLeftDClick));

    wxGetApp().m_frame->m_taskBarIcon->SetIcon(wxIcon(netpad_xpm), wxString("Netpad"));
  }

  static const wxString base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";

  static inline bool is_base64(unsigned char c) {
    return (isalnum(c) || (c == '+') || (c == '/'));
  }

  char *sample = "X42pX42p";

  wxString rle_encode(wxString& b64bytes, unsigned int iLen) 
  {
   unsigned char marker = '$';
   unsigned int i = 0, nRun;
   wxString ret, szRun;

   while (i < iLen) {
    szRun = "";
    szRun.Append(b64bytes[i++]);
    szRun.Append(b64bytes[i++]);
    szRun.Append(b64bytes[i++]);
    szRun.Append(b64bytes[i++]);    

    nRun = 1;
    
    while (i < iLen                  && 
           b64bytes[i+0] == szRun[0] &&
           i + 1 < iLen              &&
           b64bytes[i+1] == szRun[1] &&
           i + 2 < iLen              &&
           b64bytes[i+2] == szRun[2] &&
           i + 3 < iLen              &&
           b64bytes[i+3] == szRun[3] ) {

       i += 4;

       if (++nRun == 9999)
         break;
    }
    
    ret.Append(szRun);
    
    if (nRun > 1) {
      ret.Append(marker);
      ret.Append(wxString::Format(wxT("%.4i"), nRun));
    }
   }

   assert(i==iLen);

   /*
   wxString decoded = rle_decode(ret);
   assert(b64bytes == decoded);
   */

   return ret;
  }

  wxString
   rle_decode(wxString& b64bytes)
  {
    wxString *left  = new wxString(b64bytes.BeforeFirst('$'));
    wxString *right = new wxString(b64bytes.AfterFirst('$'));

    while (right->Len()) {
  
     long run = 0;
     wxString sRun;

     assert(isdigit((*right)[0]));
     sRun.Append((*right)[0]);
     assert(isdigit((*right)[1]));
     sRun.Append((*right)[1]);
     assert(isdigit((*right)[2]));
     sRun.Append((*right)[2]);
     assert(isdigit((*right)[3]));
     sRun.Append((*right)[3]);

     sRun.ToLong(&run);

     assert(run);

     wxString *toExpand = new wxString();

     toExpand->Append((*left)[left->Len()-4]);
     toExpand->Append((*left)[left->Len()-3]);
     toExpand->Append((*left)[left->Len()-2]);
     toExpand->Append((*left)[left->Len()-1]);    
    
     run--;
    
     while (run) {
      left->Append(*toExpand);
      run--;
     }

     *right = right->Remove(0,4);
    
     left->Append(right->BeforeFirst('$'));
    
     *right = right->AfterFirst('$');
    }
  
    return *left;
  }
  